﻿using huqiang.Collections;
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;

namespace huqiang.Communication
{
    public class DataPacker:DataPack32
    {
        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
        public struct MsgSendCache
        {
            /// <summary>
            /// 此消息的id 
            /// </summary>
            public UInt32 MsgID;//2
            /// <summary>
            /// 此消息的某个分卷
            /// </summary>
            public byte CurPart;//2
            /// <summary>
            /// 创建的时间
            /// </summary>
            public UInt16 CreateTime;
            /// <summary>
            /// 上一次发送的时间
            /// </summary>
            public long SendTime;
            /// <summary>
            /// 发送的次数
            /// </summary>
            public int SendCount;
            /// <summary>
            /// 数据长度
            /// </summary>
            public int Len;
            public unsafe static int Size = sizeof(MsgSendCache);
        }

        public const byte Msg_Normal = 0;
        public const byte Msg_Success = 128;
        public const byte Msg_Failed = 129;
        public const byte Msg_Heart = 130;
        /// <summary>
        /// 1488
        /// </summary>
        public static UInt16 FragmentSize = 12 * 124;
        /// <summary>
        /// 1477
        /// </summary>
        public static UInt16 FragmentDataSize = (UInt16)(12 * 124 - MsgHead.Size);// + 8 + MsgHead.Size;//(1472 - 8 - MsgHead.Size) - 12 * 4;
        
        internal static byte[] maxBuffer = new byte[65536];
        static byte[] recvBuffer = new byte[2048];
        static byte[] tmpBuffer = new byte[2048];
        static byte[] tmpBuffer2 = new byte[128];
        int delayStart;
        int delayEnd;
        UInt32 msgId = 100;
        UInt16[] delays = new UInt16[128];//时延统计
        int mip = 0;
        UInt32[] msgs = new UInt32[128];//最近接收的消息，防止重复接收
        MsgRecvCache[] msgCaches = new MsgRecvCache[128];

        internal List<byte[]> sendBuffer = new List<byte[]>();

        public int max = 384;
        public int Usage { get => sendBuffer.Count; }
        public int Remain { get => max - sendBuffer.Count; }
        /// <summary>
        /// 数据平均时延
        /// </summary>
        public int Delay
        {
            get
            {
                int a = 0;
                int i = 0;
                for (; i < 128; i++)
                {
                    if (delayStart == delayEnd)
                        break;
                    a += delays[delayStart];
                    delays[delayStart] = 0;
                    if (delayStart < 127)
                        delayStart++;
                    else delayStart = 0;
                }
                if (i == 0)
                    return 0;
                return a / i;
            }
        }
        public int AddRecvMsg(byte[] buf, int len, UInt16 time)
        {
            int start = 0;
            int end = len;
            while (GetPart(buf, ref start, ref end))
            {
                int l = end - start;
                if (l > 4 & l <= 2048)
                {
                    int c = UnpackInt32(buf, start, end, recvBuffer);
                    HandleRecv(recvBuffer, c, time);
                }
                start = end + 4;
                end = len;
            }
            return start;
        }
        public void HandleRecv(byte[] dat,int len,UInt16 time)
        {
            byte type = dat[0];
            if (type == Msg_Success)
            {
                MsgReturn head;
                unsafe
                {
                    fixed (byte* bp = &dat[0])
                        head = *(MsgReturn*)bp;
                }
                if(RemoveSendMsg(head.MsgID, head.CurPart))
                {
                    UInt16 ot = (UInt16)(time - head.Time);
                    if (ot < 0)
                        ot += 30000;
                    delays[delayEnd] = ot;
                    delayEnd++;
                    if (delayEnd >= 128)
                        delayEnd = 0;
                }
            }
            else
            {
                HandleMsg(dat, len);
            }
        }
        void HandleMsg(byte[] dat,int len)
        {
            MsgHead head;
            unsafe
            {
                fixed (byte* bp = &dat[0])
                    head = *(MsgHead*)bp;
            }
            if (head.AllPart==0 || head.AllPart > 32)
                return;
            if (head.Lenth > 32 * FragmentDataSize)//允许封包大小为32个分卷，超出的部分需自行拼接封装
                return;
            if (head.AllPart * FragmentDataSize < head.Lenth)
                return;
            if (head.CurPart >= head.AllPart)
                return;
            if (head.PartLen > FragmentDataSize)
                return;
            int s = mip;
            for (int i = 0; i < 128; i++)//检测此消息是否已经被处理过了
            {
                if (msgs[s] == head.MsgID)
                {
                    Success(ref head);//再次通知对方，此消息已接收到了
                    return;
                }
                s++;
                if (s >= 128)
                    s = 0;
            }
            if (head.AllPart == 1)
            {
                msgs[mip] = head.MsgID;
                mip++;
                if (mip >= 128)
                    mip = 0;
                try
                {
                    Dispatch(dat, MsgHead.Size, head.Lenth);
                }
                catch (Exception ex)
                {
                    OutLog.WriteLine(ex.StackTrace);
                }
            }
            else
            {
                HandleMsgPart(ref head, dat);
            }
            Success(ref head);
        }
        static bool SetChecked(ref Int32 check, int part)
        {
            int o = 1 << part;
            if ((check & o) > 0)
                return false;
            check |= o;
            return true;
        }
        void HandleMsgPart(ref MsgHead head, byte[] dat)
        {
            for (int i = 0; i < msgCaches.Length; i++)
            {
                if (msgCaches[i].msgID == head.MsgID)
                {
                    if(msgCaches[i].AllPart!=head.AllPart)//消息异常
                    {
                        msgCaches[i].Dispose();
                        return;
                    }
                    if(SetChecked(ref msgCaches[i].state,head.CurPart))
                    {
                        byte[] cache = GetBuffer(head.PartLen);
                        Array.Copy(dat, cache,head.PartLen + MsgHead.Size);
                        msgCaches[i].Data[head.CurPart] = cache;
                        msgCaches[i].rcvPart ++;
                        msgCaches[i].lastTime = KcpListener.NowMillisecond;
                        if (msgCaches[i].rcvPart>=head.AllPart)
                        {
                            int s = 0;
                            for (int j = 0; j < head.AllPart; j++)
                            {
                                int size = 0;
                                unsafe
                                {
                                    fixed (byte* bp = &msgCaches[i].Data[j][0])
                                    {
                                        size = ((MsgHead*)bp)->PartLen;
                                    }
                                }
                                Array.Copy(msgCaches[i].Data[j], MsgHead.Size, maxBuffer, s, size);
                                s += size;
                            }
                            try
                            {
                                Dispatch(maxBuffer, 0, head.Lenth);
                            }
                            catch (Exception ex)
                            {
                                OutLog.WriteLine(ex.StackTrace);
                            }
                            msgs[mip] = head.MsgID;
                            mip++;
                            if (mip >= 64)
                                mip = 0;
                            msgCaches[i].Clear();
                        }
                    }
                    return;
                }
            }
            for (int i = 0; i < msgCaches.Length; i++)
            {
                if (msgCaches[i].msgID == 0)
                {
                    msgCaches[i].msgID = head.MsgID;
                    msgCaches[i].AllPart = head.AllPart;
                    msgCaches[i].rcvPart = 1;
                    SetChecked(ref msgCaches[i].state,head.CurPart);
                    byte[] cache = GetBuffer(head.PartLen);
                    Array.Copy(dat, cache, head.PartLen + MsgHead.Size);
                    msgCaches[i].Data = GetBuffer2();
                    msgCaches[i].Data[head.CurPart] = cache;
                    msgCaches[i].lastTime = KcpListener.NowMillisecond;
                    break;
                }
            }
        }
        public void ClearTimeOut(int timeout)
        {
            for(int i=0;i<msgCaches.Length;i++)
            {
                if (msgCaches[i].msgID != 0)
                    if (msgCaches[i].lastTime - KcpListener.NowMillisecond >= timeout)
                        msgCaches[i].Dispose();
            }
        }
        void Success(ref MsgHead head)
        {
            unsafe
            {
                fixed (byte* bp = &tmpBuffer[0])
                {
                    MsgReturn* hp = (MsgReturn*)bp;
                    hp->Type = Msg_Success;
                    hp->MsgID = head.MsgID;
                    hp->CurPart = head.CurPart;
                    hp->Time = head.Time;
                }
            }
            PackInt32(tmpBuffer, MsgReturn.Size, out int len, tmpBuffer2);
            FirstSend(tmpBuffer2,0, len);
        }
        public bool RemoveSendMsg(UInt32 msgId, byte part)
        {
            for (int i = 0; i < sendBuffer.Count; i++)
            {
                unsafe
                {
                    fixed (byte* bp = &sendBuffer[i][0])
                    {
                        MsgSendCache* mp = (MsgSendCache*)bp;
                        if (mp->MsgID == msgId)
                            if (mp->CurPart == part)
                            {
                                RecycleBuffer(sendBuffer[i]);
                                sendBuffer.RemoveAt(i);
                                return true;
                            }
                    }
                }
            }
            return false;
        }
        public void AddSendMsg(byte[] dat, UInt16 time)
        {
            if (dat.Length > FragmentDataSize*32)
            {
                OutLog.WriteLine("超出封包数据最大长度");
                return;
            }
            msgId++;
            PackMsg(Msg_Normal, dat, msgId, time);
        }
        void PackMsg(byte msgType, byte[] dat, UInt32 msgId, UInt16 time)
        {
            int len = dat.Length;
            int p = len / FragmentDataSize;
            int r = len % FragmentDataSize;
            int all = p;//总计分卷数量
            if (r > 0)
                all++;
            int s = 0;
            for (int i = 0; i < p; i++)
            {
                unsafe
                {
                    fixed (byte* bp = &tmpBuffer[0])
                    {
                        MsgHead* head = (MsgHead*)bp;
                        head->Type = 0;
                        head->MsgID = msgId;
                        head->CurPart = (byte)i;
                        head->AllPart = (byte)all;
                        head->PartLen = (UInt16)FragmentDataSize;
                        head->Lenth = (UInt16)len;
                        head->Time = time;
                        byte* dp = bp + MsgHead.Size;
                        for (int j = 0; j <FragmentDataSize; j++)
                        {
                            dp[j] = dat[s];
                            s++;
                        }
                    }
                }
                int index = PackSingle(tmpBuffer, FragmentSize, msgId,(byte)i, time);
                //sendBuffer.SendTo(soc, endpPoint, time, index);
                //indexs[i] = index;
            }
            if (r > 0)
            {
                unsafe
                {
                    fixed (byte* bp = &tmpBuffer[0])
                    {
                        MsgHead* head = (MsgHead*)bp;
                        head->Type = 0;
                        head->MsgID = msgId;
                        head->CurPart = (byte)p;
                        head->AllPart = (byte)all;
                        head->PartLen = (UInt16)r;
                        head->Lenth = (UInt16)len;
                        head->Time = time;
                        byte* dp = bp + MsgHead.Size;
                        for (int j = 0; j < r; j++)
                        {
                            dp[j] = dat[s];
                            s++;
                        }
                    }
                }
                int index = PackSingle(tmpBuffer, r + MsgHead.Size, msgId, (byte)p, time);
                //sendBuffer.SendTo(soc, endpPoint, time, index);
                //indexs[p] = index;
            }
        }
        int PackSingle(byte[] dat,  int len, UInt32 msgId, byte part, UInt16 time)
        {
            byte[] buf = null;
            int l = PackInt32(dat, len, ref buf, MsgSendCache.Size);
            unsafe
            {
                fixed (byte* bp = &buf[0])
                {
                    MsgSendCache* mp = (MsgSendCache*)bp;
                    mp->MsgID = msgId;
                    mp->CurPart = part;
                    mp->CreateTime = time;
                    mp->SendTime = KcpListener.NowMillisecond;
                    mp->SendCount = 0;
                    mp->Len = l;
                }
            }
            int t = sendBuffer.Count;
            sendBuffer.Add(buf);
            FirstSend(buf, MsgSendCache.Size, l);
            return t;
        }
        public virtual void FirstSend(byte[] dat, int startindex, int len)
        {
        }
        public virtual void Dispatch(byte[] buf, int start, int len)
        {
        }
        public void Clear()
        {
            for(int i=0;i<msgCaches.Length;i++)
            {
                msgCaches[i].Dispose();
            }
            for (int i = 0; i < sendBuffer.Count; i++)
                RecycleBuffer(sendBuffer[i]);
            sendBuffer.Clear();
        }
    }
}
